package com.xinlong.shop.framework.weixin.service;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.xinlong.shop.framework.cache.ICache;
import com.xinlong.shop.framework.exception.ServiceException;
import com.xinlong.shop.framework.service.ISysSettingService;
import com.xinlong.shop.framework.setting.XinLongConfig;
import com.xinlong.shop.framework.util.JsonUtil;
import com.xinlong.shop.framework.util.StringUtil;
import com.xinlong.shop.framework.weixin.config.WeiXinMpConfig;
import com.xinlong.shop.framework.weixin.config.WeiXinPayConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Sylow
 * @Description
 * @Date: Created in 18:03 2023/1/3
 */
@Component
public class WeiXinMpService {

    private final static Logger logger = LoggerFactory.getLogger(WeiXinMpService.class);

    private final WeiXinMpConfig weiXinMpConfig;
    private final WeiXinPayConfig weiXinPayConfig;
    private final ICache cache;
    private final XinLongConfig xinLongConfig;
    private final ISysSettingService sysSettingService;
    // 微信配置 需要单例
    private RSAAutoCertificateConfig config;


    private static final String ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN_KEY";
    private static final String TICKET_KEY = "WX_TICKET_KEY";
    public WeiXinMpService(WeiXinMpConfig weiXinMpConfig, WeiXinPayConfig weiXinPayConfig, ICache cache, XinLongConfig xinLongConfig, ISysSettingService sysSettingService) {
        this.weiXinMpConfig = weiXinMpConfig;
        this.weiXinPayConfig = weiXinPayConfig;
        this.cache = cache;
        this.xinLongConfig = xinLongConfig;
        this.sysSettingService = sysSettingService;
    }

    public String getTicket() {
        Object ticketCache = cache.get(TICKET_KEY);
        // 如果缓存中有，没过期，直接返回。
        if (ticketCache != null) {
            logger.debug("从缓存中获取ticket:{}", ticketCache);
            return ticketCache.toString();
        }

        String accessToken = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";
        Map<String, Object> result = sendGet(url);
        if (result.containsKey("ticket")) {
            String ticket = (String) result.get("ticket");
            // 过期时间 提前60秒过期
            int expireTime = Double.valueOf(result.get("expires_in").toString()).intValue() - 60;
            // 得到后存入缓存
            this.cache.put(TICKET_KEY, ticket, expireTime);
            return ticket;
        } else {
            logger.error("获取微信ticket失败，错误码：{}，错误信息：{}", result.get("errcode"), result.get("errmsg"));
            return null;
        }
    }

    public String getAccessToken() {
        Object accessTokenCache = cache.get(ACCESS_TOKEN_KEY);
        // 如果缓存中有，没过期，直接返回。
        if (accessTokenCache != null) {
            logger.debug("从缓存中获取accessToken:{}", accessTokenCache);
            return accessTokenCache.toString();
        }
        String appId = weiXinMpConfig.getAppid();
        String appSecret = weiXinMpConfig.getAppSecret();
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
        Map<String, Object> result = sendGet(url);
        if (result.containsKey("access_token")) {
            String accessToken = result.get("access_token").toString();
            // 过期时间 提前60秒过期
            int expireTime = Double.valueOf(result.get("expires_in").toString()).intValue() - 60;
            // 得到后存入缓存
            this.cache.put(ACCESS_TOKEN_KEY, accessToken, expireTime);
            return accessToken;
        } else {
           logger.error("获取微信公众号access_token失败：{}", result);
           return null;
        }
    }

    public String getOpenId(String code) {
        //https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        String appId = weiXinMpConfig.getAppid();
        String appSecret = weiXinMpConfig.getAppSecret();
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                + appId + "&secret="
                + appSecret + "&code=" + code + "&grant_type=authorization_code";
        Map<String, Object> result = sendGet(url);
        if (result.containsKey("openid")) {
            String openId = result.get("openid").toString();
            return openId;
        } else {
            logger.error("获取微信openId失败：{}", result);
        }
        return null;
    }

    public PrepayWithRequestPaymentResponse pay(String orderId, String openId, BigDecimal price, String goodsName) {
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        String appId = weiXinMpConfig.getAppid();
        String mchId = weiXinPayConfig.getMchid();
        Config config = this.getConfig();

        // 拼凑订单号  给微信传递的订单号 要增加随机数 防止订单重复
        orderId = orderId + "_" + StringUtil.random(1000);


        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        // 单位为分
        Integer total = NumberUtil.mul(price, 100).intValue();
        amount.setTotal(total);
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(mchId);
        request.setDescription(goodsName);
        request.setNotifyUrl(xinLongConfig.getBuyerDomain() + "/buyer/wx/pay/callback");
        request.setOutTradeNo(orderId);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);
        // request.setAttach(); // 自定义参数 后期区分订单类型 可用
        // response包含了调起支付所需的所有参数，可直接用于前端调起支付
        try {
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
            return response;
        } catch (Exception e) {
            logger.error("微信支付错误：{}",e.getMessage());
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     *
     * @param wechatPayCertificateSerialNumber
     * @param signature
     * @param timestamp
     * @param nonce
     * @param requestBody
     * @return 验证成功返回成功支付的订单单号和支付单号
     */
    public Map<String, String> callBack(String wechatPayCertificateSerialNumber,
                           String signature,
                           String timestamp,
                           String nonce,
                           String requestBody){

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPayCertificateSerialNumber)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(requestBody)
                .build();


        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);

        // 以支付通知回调为例，验签、解密并转换成 Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);

        String result = transaction.getTradeState().toString();
        if ("SUCCESS".equals(result)) {
            Map<String, String> map = new HashMap<>();

            String orderSn = transaction.getOutTradeNo();
            orderSn = orderSn.substring(0, orderSn.lastIndexOf("_"));
            map.put("orderSn", orderSn);
            map.put("payOrderSn",transaction.getTransactionId());
            return map;
        }

        return null;
    }

    private Map sendGet(String url) {
        String result = HttpRequest.get(url).execute().body();

        if (StrUtil.isNotBlank(result)) {
            Map resultMap = JsonUtil.jsonToMap(result);
            return resultMap;
        }
        return null;
    }

    @Autowired
    public void setConfig(){
//        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
//        String appId = weiXinMpConfig.getAppid();
        Map<String, Object> settings = sysSettingService.getSetting();
        boolean openWeixinPay = (boolean)settings.get("openWeixinPay");
        if (openWeixinPay) {
            String mchId = weiXinPayConfig.getMchid();
            String key = weiXinPayConfig.getKey();
            String serialNumber = weiXinPayConfig.getSerialNumber();
            String apiv3Key = weiXinPayConfig.getApiKey();
            config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(mchId)
                            .privateKeyFromPath(key)
                            .merchantSerialNumber(serialNumber)
                            .apiV3Key(apiv3Key)
                            .build();
        }
    }

    public Config getConfig(){
        return this.config;
    }

    public static void main(String[] args) {
//        String appId = "wx98be844de541a102";
//        String appSecret = "0028ea9addbf29800aa9c258b2817b52";
//        WeiXinMpConfig weiXinMpConfig = new WeiXinMpConfig();
//        weiXinMpConfig.setAppid(appId);
//        weiXinMpConfig.setAppSecret(appSecret);
//        WeiXinMpService wx = new WeiXinMpService(weiXinMpConfig, null);
//        String accessToken = wx.getAccessToken();
//        System.out.println(accessToken);

        //System.out.println(DateUtil.currentSeconds());
        System.out.println(StringUtil.random(10000));

        String orderSn = "2021072210000001_123";
        orderSn = orderSn.substring(0, orderSn.lastIndexOf("_"));
        System.out.println(orderSn);
    }

}
